package com.yoogor.xposedtest.hook;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.webkit.ValueCallback;

import com.yoogor.xposedtest.FileAction;
import com.yoogor.xposedtest.JsoupParse;
import com.yoogor.xposedtest.MMMessageEntity;
import com.yoogor.xposedtest.WebsocketConn;

import org.apache.commons.lang.StringEscapeUtils;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

/**
 * Created by paron on 2018/7/15.
 */

public class MMHook {

    enum HOOK_TYPE {
        TYPE_ACTIVITY_CREATE,
        TYPE_ACTIVITY_RESUME,
        TYPE_ACTIVITY_PAUSE,
    }

    public static String processName = "";

    private static Activity currentActivty = null;
    private static String qrcode = "";
    private static String gongzhonghaoname = "";
    private static boolean stoped = false;
    private static boolean shouldFinish = false;

    public static String getProcessName(Context cxt, int pid) {
        ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
        if (runningApps == null) {
            return null;
        }
        for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
            if (procInfo.pid == pid) {
                return procInfo.processName;
            }
        }
        return null;
    }

    private static void copyPrivateDir(Context context) {
        XposedBridge.log("exist:" + new File("/data/data/io.va.exposed/virtual/data/data/com.tencent.mm/files/public").exists() + ":" + context.getExternalFilesDir("temp_weixin"));
        XposedBridge.log("result:" + FileAction.copyFolder("/data/data/io.va.exposed/virtual/data/data/com.tencent.mm/files/public", context.getExternalFilesDir("temp_weixin").getAbsolutePath()));
//        XposedBridge.log("result:" + FileAction.copyFolder(context.getFilesDir().getAbsolutePath(), "/storage/sdcard0/Documents/temp"));
    }

    static Map<HOOK_TYPE, CopyOnWriteArrayList<MMHookEntity>> hookEntityMap = new HashMap<>();

    public static void init(final XC_LoadPackage.LoadPackageParam lpparam) {
        Class activityBaseClass = XposedHelpers.findClass("android.app.Activity", lpparam.classLoader);
        final Class webviewActivity = XposedHelpers.findClass("com.tencent.mm.plugin.webview.ui.tools.WebViewUI", lpparam.classLoader);
        hookActivityLifecircle(activityBaseClass, lpparam);
        hookMessage223Constructor(lpparam);
        hookMessage223Constructor_1(lpparam);
        hookMessage372Contructor(lpparam);
        hookScanerUtilMethod(lpparam);
        hookWebstubServiceMethods(lpparam);
        hookWebviewActivity(lpparam);
        hookContactUIActivityClick(lpparam);
        hookReportMethods(lpparam);

        final MMHookEntity hookFtsSearchMainResume = new MMHookEntity(HOOK_TYPE.TYPE_ACTIVITY_RESUME, "com.tencent.mm.plugin.fts.ui.FTSMainUI") {
            @Override
            protected void exec(XC_MethodHook.MethodHookParam param) {
                if (!TextUtils.isEmpty(gongzhonghaoname)) {
                    XposedHelpers.setObjectField(param.thisObject, "eFb", gongzhonghaoname);
                    XposedHelpers.callStaticMethod(param.thisObject.getClass(), "c", param.thisObject);
                }
            }
        };
        hookFtsSearchMainResume.onlyOnece = true;
        final MMHookEntity hookEntityActivityResume1 = new MMHookEntity(HOOK_TYPE.TYPE_ACTIVITY_RESUME) {

            @Override
            protected void exec(XC_MethodHook.MethodHookParam param) {
                if (!TextUtils.isEmpty(qrcode) && currentActivty != null && !webviewActivity.getName().equals(param.thisObject.getClass().getName())) {
                    Intent intent = new Intent();
                    intent.setClassName("com.tencent.mm", "com.tencent.mm.plugin.scanner.ui.BaseScanUI");
                    currentActivty.startActivity(intent);
                } else if (!TextUtils.isEmpty(gongzhonghaoname) && currentActivty != null && !webviewActivity.getName().equals(param.thisObject.getClass().getName())) {
                    Intent intent = new Intent();
                    intent.setClassName("com.tencent.mm", "com.tencent.mm.plugin.fts.ui.FTSMainUI");
                    currentActivty.startActivity(intent);
                }
            }
        };
        hookEntityActivityResume1.onlyOnece = true;
        final MMHookEntity scanUIResume = new MMHookEntity(HOOK_TYPE.TYPE_ACTIVITY_RESUME, "com.tencent.mm.plugin.scanner.ui.BaseScanUI") {
            @Override
            protected void exec(XC_MethodHook.MethodHookParam param) {
                if (!TextUtils.isEmpty(qrcode)) {
                    Object e = XposedHelpers.newInstance(XposedHelpers.findClass("com.tencent.mm.plugin.scanner.util.e", lpparam.classLoader));
                    XposedHelpers.setObjectField(param.thisObject, "quj", e);
                    XposedHelpers.setObjectField(e, "mActivity", param.thisObject);
                    XposedHelpers.callMethod(e, "a", param.thisObject, 0, qrcode, false);
                }
            }
        };
        scanUIResume.onlyOnece = true;
        final MMHookEntity contactUIOnResume = new MMHookEntity(HOOK_TYPE.TYPE_ACTIVITY_RESUME, "com.tencent.mm.plugin.profile.ui.ContactInfoUI") {
            @Override
            protected void exec(final XC_MethodHook.MethodHookParam param) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        int i = 0;
                        try {
                            while (i < 10) {
                                final Object pFT = XposedHelpers.getObjectField(param.thisObject, "pFT");
                                if (pFT != null) {
                                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                                        @Override
                                        public void run() {
                                            XposedHelpers.callMethod(pFT, "wT", "contact_info_bizinfo_external#0");
                                        }
                                    });
                                    break;
                                }
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        } catch (Exception e) {

                        }
                    }
                }).start();
            }
        };
        contactUIOnResume.onlyOnece = true;
        MMHookEntity webviewUIOnResume = new MMHookEntity(HOOK_TYPE.TYPE_ACTIVITY_RESUME, webviewActivity.getName()) {
            @Override
            protected void exec(XC_MethodHook.MethodHookParam param) {
                WebsocketConn.sendMessage("scrollLength:");
                final Object pRV = XposedHelpers.getObjectField(param.thisObject, "pRV");
                final Object webview = XposedHelpers.callMethod(pRV, "getView");
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        final AtomicInteger i = new AtomicInteger(0);
                        final AtomicInteger lastScrollLength = new AtomicInteger(0);
                        final Handler mainHander = new Handler(Looper.getMainLooper());
                        while (i.incrementAndGet() < 10) {
                            mainHander.post(new Runnable() {
                                @Override
                                public void run() {
                                    int scrollLength = (int) XposedHelpers.callMethod(webview, "computeVerticalScrollRange");
                                    WebsocketConn.sendMessage("scrollLength:" + scrollLength);
                                    XposedHelpers.callMethod(webview, "scrollBy", 0, 10000);
                                    if (lastScrollLength.get() != scrollLength) {
                                        i.set(0);
                                    }
                                    lastScrollLength.set(scrollLength);
                                }
                            });
                            try {
                                if (stoped || (!TextUtils.isEmpty(gongzhonghaoname) && lastScrollLength.get() > 2200)) {
                                    break;
                                }
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        // 滚动到底，获取网页内容
                        mainHander.post(new Runnable() {
                            @Override
                            public void run() {
                                XposedHelpers.callMethod(pRV, "evaluateJavascript", "document.getElementsByTagName('html')[0].innerHTML",
                                        new ValueCallback<String>() {
                                            @Override
                                            public void onReceiveValue(String value) {
                                                final String content = StringEscapeUtils.unescapeJavaScript(StringEscapeUtils.unescapeHtml(value));
                                                new Thread(new Runnable() {
                                                    @Override
                                                    public void run() {
                                                        try {
                                                            List<MMMessageEntity> mmMessageEntities1 = JsoupParse.parse(content);
                                                            if (mmMessageEntities1 != null && !mmMessageEntities1.isEmpty()) {
                                                                JSONArray jsonArray = new JSONArray();
                                                                for (int i = 0; i < mmMessageEntities1.size(); i++) {
                                                                    JSONObject jsonObject = new JSONObject();
                                                                    jsonObject.put("url", mmMessageEntities1.get(i).getUrl());
                                                                    jsonObject.put("title", mmMessageEntities1.get(i).getTitle());
                                                                    jsonObject.put("shortcat", mmMessageEntities1.get(i).getShortcat());
                                                                    jsonObject.put("extra", mmMessageEntities1.get(i).getExtraInfo());
                                                                    jsonObject.put("desc", mmMessageEntities1.get(i).getDesc());
                                                                    jsonObject.put("data-type", mmMessageEntities1.get(i).getDataType());
                                                                    jsonObject.put("html", mmMessageEntities1.get(i).getContentHtml());
                                                                    jsonArray.put(jsonObject);
                                                                }
                                                                WebsocketConn.sendMessage("data:" + jsonArray.toString());
                                                                WebsocketConn.sendMessage("completed");
                                                            }
                                                        } catch (Exception e) {
                                                            WebsocketConn.sendMessage(e.getMessage());
                                                        }
                                                    }
                                                }).start();
                                            }
                                        });
                            }
                        });
                    }
                }).start();
            }
        };
        MMHookEntity hookEntityActivityResume = new MMHookEntity(HOOK_TYPE.TYPE_ACTIVITY_RESUME) {

            @Override
            protected void exec(XC_MethodHook.MethodHookParam param) {
//                if (currentActivty == null && "com.tencent.mm".equals(processName)) {
//                    copyPrivateDir((Activity) param.thisObject);
//                }
                currentActivty = (Activity) param.thisObject;
                WebsocketConn.sendMessage("data:" + getProcessName(currentActivty, android.os.Process.myPid()));
//                if ("com.tencent.mm.ui.LauncherUI" != currentActivty.getClass().getName() && "com.tencent.mm.plugin.fts.ui.FTSMainUI" != currentActivty.getClass().getName() && shouldFinish) {
//                    currentActivty.onBackPressed();
//                } else if ("com.tencent.mm.ui.LauncherUI".equals(currentActivty.getClass().getName()) && shouldFinish) {
//                    shouldFinish = false;
//                    addHookEntity(hookFtsSearchMainResume);
//                    addHookEntity(contactUIOnResume);
//                    Intent intent = new Intent();
//                    intent.setClassName("com.tencent.mm", "com.tencent.mm.plugin.fts.ui.FTSMainUI");
//                    currentActivty.startActivity(intent);
//                }
            }
        };

        addHookEntity(hookEntityActivityResume);

//        addHookEntity(hookEntityActivityResume1);
//
//        addHookEntity(scanUIResume);
//
//        addHookEntity(contactUIOnResume);

        addHookEntity(webviewUIOnResume);
        WebsocketConn.setOnReloadListener(new WebsocketConn.MessageListerner() {
            @Override
            public void onMessage(JSONObject jsonObject) {
                if (TextUtils.equals(jsonObject.optString("key"), "url")) {
                    qrcode = jsonObject.optString("url");
                    gongzhonghaoname = "";
                    stoped = false;
                    addHookEntity(scanUIResume);
                    addHookEntity(contactUIOnResume);
                    if (currentActivty != null && currentActivty.getClass().getName().contains("WebViewUI")) {
                        addHookEntity(hookEntityActivityResume1);
                        currentActivty.onBackPressed();
                    } else if (currentActivty != null) {
                        Intent intent = new Intent();
                        intent.setClassName("com.tencent.mm", "com.tencent.mm.plugin.scanner.ui.BaseScanUI");
                        currentActivty.startActivity(intent);
                    }
                } else if (TextUtils.equals(jsonObject.optString("key"), "stop")) {
                    if (currentActivty != null && currentActivty.getClass().getName().contains("WebViewUI")) {
                        stoped = true;
                    }
                } else if (TextUtils.equals(jsonObject.optString("key"), "gongzhonghao_name")) {
                    qrcode = "";
                    gongzhonghaoname = jsonObject.optString("gongzhonghao_name", "");
                    stoped = false;
//                    if ("com.tencent.mm.ui.LauncherUI".equals(currentActivty.getClass().getName())) {
                    addHookEntity(hookFtsSearchMainResume);
                    addHookEntity(contactUIOnResume);
                    Intent intent = new Intent();
                    intent.setClassName("com.tencent.mm", "com.tencent.mm.plugin.fts.ui.FTSMainUI");
                    currentActivty.startActivity(intent);
//                    } else {
//                        shouldFinish = true;
//                        currentActivty.onBackPressed();
//                    }
//                    if (currentActivty != null && currentActivty.getClass().getName().contains("WebViewUI")) {
//                        addHookEntity(hookEntityActivityResume1);
//                        currentActivty.onBackPressed();
//                    } else if (currentActivty != null) {
//                        Intent intent = new Intent();
//                        intent.setClassName("com.tencent.mm", "com.tencent.mm.plugin.fts.ui.FTSMainUI");
//                        currentActivty.startActivity(intent);
//                    }
                }
            }
        });
    }

    private static void addHookEntity(MMHookEntity entity) {
        if (!hookEntityMap.containsKey(entity.hook_type)) {
            hookEntityMap.put(entity.hook_type, new CopyOnWriteArrayList<MMHookEntity>());
        }
        if (!hookEntityMap.get(entity.hook_type).contains(entity)) {
            hookEntityMap.get(entity.hook_type).add(entity);
        }
    }

    /**
     * g(String,String,int,int,byte[])
     *
     * @param lpparam
     */
    private static void hookMessage223Constructor(XC_LoadPackage.LoadPackageParam lpparam) {

        XposedHelpers.findAndHookConstructor("com.tencent.mm.modelsimple.g", lpparam.classLoader, String.class, String.class, int.class, int.class, byte[].class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                WebsocketConn.sendMessage("new g:" + String.format("%s,%s,%d,%d", param.args[0], param.args[1], param.args[2], param.args[3]));
            }
        });
    }

    /**
     * 群二维码有用
     * g(String,String,int,int,byte[])
     *
     * @param lpparam
     */
    private static void hookMessage223Constructor_1(XC_LoadPackage.LoadPackageParam lpparam) {
        XposedHelpers.findAndHookConstructor("com.tencent.mm.modelsimple.g", lpparam.classLoader, String.class, int.class, int.class, int.class, String.class, int.class, byte[].class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                WebsocketConn.sendMessage("new g:" + String.format("%s,%d,%d,%d,%s,%d", param.args[0], param.args[1], param.args[2], param.args[3], param.args[4], param.args[5]));
            }
        });
    }

    private static void hookMessage372Contructor(XC_LoadPackage.LoadPackageParam lpparam) {
        XposedHelpers.findAndHookConstructor("com.tencent.mm.openim.b.e", lpparam.classLoader, String.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                WebsocketConn.sendMessage("new e:" + String.format("%s", param.args[0]));
            }
        });
    }

    private static void hookContactUIActivityClick(XC_LoadPackage.LoadPackageParam lpparam) {
        XposedHelpers.findAndHookMethod("com.tencent.mm.plugin.profile.ui.c", lpparam.classLoader, "wT", String.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                WebsocketConn.sendMessage("wT():" + param.args[0]);
            }
        });
    }

    private static void hookActivityLifecircle(Class activityBaseClass, XC_LoadPackage.LoadPackageParam lpparam) {
        XposedHelpers.findAndHookMethod(activityBaseClass, "onCreate", Bundle.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                WebsocketConn.sendMessage("in onCreate:" + param.thisObject.getClass().toString());
                Intent intent = (Intent) XposedHelpers.callMethod(param.thisObject, "getIntent");
                if (intent != null) {
                    WebsocketConn.sendMessage("bundle:" + intent.toURI());
                }
                super.beforeHookedMethod(param);
                if (hookEntityMap.containsKey(HOOK_TYPE.TYPE_ACTIVITY_CREATE)) {
                    Iterator<MMHookEntity> iterable = hookEntityMap.get(HOOK_TYPE.TYPE_ACTIVITY_CREATE).listIterator();
                    while (iterable.hasNext()) {
                        iterable.next().start(param);
                    }
                }
            }
        });
        XposedHelpers.findAndHookMethod(activityBaseClass, "onResume", new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                WebsocketConn.sendMessage("in onResume:" + param.thisObject.getClass().toString());
//                    Intent intent = (Intent) XposedHelpers.callMethod(param.thisObject, "getIntent");
//                    if (intent != null) {
//                        WebsocketConn.sendMessage("bundle:" + intent.toURI());
//                    }
                super.beforeHookedMethod(param);
                if (hookEntityMap.containsKey(HOOK_TYPE.TYPE_ACTIVITY_RESUME)) {
                    Iterator<MMHookEntity> iterable = hookEntityMap.get(HOOK_TYPE.TYPE_ACTIVITY_RESUME).listIterator();
                    while (iterable.hasNext()) {
                        iterable.next().start(param);
                    }
                }
            }
        });
        XposedHelpers.findAndHookMethod(activityBaseClass, "onPause", new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                WebsocketConn.sendMessage("in onPause:" + param.thisObject.getClass().toString());
//                    Intent intent = (Intent) XposedHelpers.callMethod(param.thisObject, "getIntent");
//                    if (intent != null) {
//                        WebsocketConn.sendMessage("bundle:" + intent.toURI());
//                    }
                super.beforeHookedMethod(param);
                if (hookEntityMap.containsKey(HOOK_TYPE.TYPE_ACTIVITY_PAUSE)) {
                    Iterator<MMHookEntity> iterable = hookEntityMap.get(HOOK_TYPE.TYPE_ACTIVITY_PAUSE).listIterator();
                    while (iterable.hasNext()) {
                        iterable.next().start(param);
                    }
                }
            }
        });
    }

    private static void hookScanerUtilMethod(XC_LoadPackage.LoadPackageParam lpparam) {
        Class stubClass = XposedHelpers.findClass("com.tencent.mm.plugin.scanner.util.e", lpparam.classLoader);
        XposedHelpers.findAndHookMethod(stubClass, "a", Activity.class, int.class, String.class, boolean.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                WebsocketConn.sendMessage("search for result:" + String.format("%s,%d,%s,%s", param.args[0].getClass().toString(), param.args[1], param.args[2], String.valueOf(param.args[3])));
            }
        });
        XposedHelpers.findAndHookMethod(stubClass, "a", int.class, int.class, String.class, XposedHelpers.findClass("com.tencent.mm.ac.l", lpparam.classLoader), new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Object object = param.args[param.args.length - 1];
                if (233 == (int) XposedHelpers.callMethod(object, "getType")) {
                    WebsocketConn.sendMessage("QE:" + XposedHelpers.callMethod(object, "QE") + "\tQI():" + XposedHelpers.callMethod(object, "QI"));
//                        if (atomicInteger.incrementAndGet() >= 50) {
//                            atomicInteger.set(0);
//                            WebsocketConn.sendMessage("发送广播");
//                            WebsocketConn.sendMessage("completed");
//                        }
                } else if (106 == (int) XposedHelpers.callMethod(object, "getType")) {
                    WebsocketConn.sendMessage("type:" + (int) XposedHelpers.callMethod(object, "getType"));
                } else {
                    WebsocketConn.sendMessage("type:" + (int) XposedHelpers.callMethod(object, "getType"));
                }
//                    if (object.getClass().toString().equals("com.tencent.mm.ac.l")) {
//
//                    } else {
//                        WebsocketConn.sendMessage("not type com.tencent.mm.ac.l:" + object.getClass().toString());
//                    }
            }
        });
    }

    private static void hookWebstubServiceMethods(XC_LoadPackage.LoadPackageParam lpparam) {
        Class stubServiceClass = XposedHelpers.findClass("com.tencent.mm.plugin.webview.stub.WebViewStubService", lpparam.classLoader);
        XposedHelpers.findAndHookMethod(stubServiceClass, "a", int.class, int.class, String.class, XposedHelpers.findClass("com.tencent.mm.ac.l", lpparam.classLoader), new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Object object = param.args[param.args.length - 1];
                if (233 == (int) XposedHelpers.callMethod(object, "getType")) {
                    WebsocketConn.sendMessage("QE:" + XposedHelpers.callMethod(object, "QE") + "\tQI():" + XposedHelpers.callMethod(object, "QI"));
//                        if (atomicInteger.incrementAndGet() >= 50) {
//                            atomicInteger.set(0);
//                            WebsocketConn.sendMessage("发送广播");
//                            WebsocketConn.sendMessage("completed");
//                        }
                } else if (106 == (int) XposedHelpers.callMethod(object, "getType")) {
                    WebsocketConn.sendMessage("type:" + (int) XposedHelpers.callMethod(object, "getType"));
                } else {
                    WebsocketConn.sendMessage("type:" + (int) XposedHelpers.callMethod(object, "getType"));
                }
//                    if (object.getClass().toString().equals("com.tencent.mm.ac.l")) {
//
//                    } else {
//                        WebsocketConn.sendMessage("not type com.tencent.mm.ac.l:" + object.getClass().toString());
//                    }
            }
        });
        //            Class stubInterfaceClass = XposedHelpers.findClass("com.tencent.mm.plugin.webview.stub.d$a$a", lpparam.classLoader);
//            Class stubServiceClass1 = XposedHelpers.findClass("com.tencent.mm.plugin.webview.stub.WebViewStubService$1", lpparam.classLoader);
//            XposedHelpers.findAndHookMethod(stubServiceClass1, "s", int.class, Bundle.class, new XC_MethodHook() {
//                @Override
//                protected void afterHookedMethod(final MethodHookParam param) throws Throwable {
//                    super.afterHookedMethod(param);
//                    if (233 == (int) param.args[0]) {
//                        Bundle bundle = (Bundle) param.args[1];
//                        _bundle.putAll(bundle);
//                        Intent intent = new Intent();
//                        if (bundle != null) {
//                            intent.putExtras(bundle);
//                        }
////                        WebsocketConn.sendMessage("stubServiceClass bundle:" + intent.toURI());
//
//                    }
//                }
//            });
//        XposedHelpers.findAndHookMethod(stubInterfaceClass, "s", int.class, Bundle.class, new XC_MethodHook() {
//                @Override
//                protected void afterHookedMethod(final MethodHookParam param) throws Throwable {
//                    super.afterHookedMethod(param);
//                    if (233 == (int) param.args[0]) {
//                        Bundle bundle = (Bundle) param.args[1];
//                        Intent intent = new Intent();
//
//                        if (bundle != null) {
//                            _bundle.putAll(bundle);
//                            intent.putExtras(bundle);
//                        }
//
////                        WebsocketConn.sendMessage("bundle:" + intent.toURI());
////                        if (mmMessageEntities != null && !mmMessageEntities.isEmpty()) {
////                            for (MMMessageEntity mmMessageEntity : mmMessageEntities) {
////                                final Bundle bundle1 = new Bundle();
////                                bundle1.putAll(_bundle);
//////                            bundle1.putInt("webview_binder_id", hashCode());
////                                bundle1.putString("geta8key_data_req_url", mmMessageEntity.getUrl());
//////                        int binderId = _bundle != null ? _bundle.getInt("webview_binder_id") : 0;
////                                String url1 = mmMessageEntities.get(0).getUrl();
//////                        XposedHelpers.callMethod(jjz, "cB", url1, binderId);
//////                        bundle1.putInt("geta8key_session_id", (int) System.currentTimeMillis());
//////                                mmMessageEntities.clear();
//////                            new Thread(new Runnable() {
//////                                @Override
//////                                public void run() {
//////                                    try {
//////                                        Thread.sleep(1000);
//////                                    } catch (InterruptedException e) {
//////                                        e.printStackTrace();
//////                                    }
////                                Bundle bundle2 = new Bundle();
////                                bundle2.putInt("scene_end_type", 233);
////                                bundle2.putInt("scene_end_listener_hash_code", _bundle.getInt("webview_binder_id"));
////                                XposedHelpers.callMethod(param.thisObject, "a", 5, bundle2, _bundle.getInt("webview_binder_id"));
////                                XposedHelpers.callMethod(param.thisObject, "s", 233, bundle1);
////                            }
////                                }
////                            }).start();
////                        }
//                    }
//                }
//            });
    }

    private static void hookReportMethods(XC_LoadPackage.LoadPackageParam lpparam) {
        Class exceptionClass = XposedHelpers.findClass("com.tencent.mm.plugin.report.service.f", lpparam.classLoader);
        XposedHelpers.findAndHookMethod(exceptionClass, "b", int.class, String.class, boolean.class, boolean.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.setResult(null);
            }
        });
        XposedHelpers.findAndHookMethod(exceptionClass, "d", int.class, int.class, int.class, boolean.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.setResult(null);
            }
        });
        XposedHelpers.findAndHookMethod(exceptionClass, "d", ArrayList.class, boolean.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.setResult(null);
            }
        });
        Class kvreport = XposedHelpers.findClass("com.tencent.mm.plugin.report.service.e", lpparam.classLoader);
        XposedHelpers.findAndHookMethod(kvreport, "a", long.class, String.class, boolean.class, boolean.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.setResult(null);
            }
        });
        XposedHelpers.findAndHookMethod(kvreport, "a", long.class, byte[].class, boolean.class, boolean.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.setResult(null);
            }
        });
        XposedHelpers.findAndHookMethod(kvreport, "b", long.class, long.class, long.class, boolean.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.setResult(null);
            }
        });
        XposedHelpers.findAndHookMethod(kvreport, "c", ArrayList.class, boolean.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.setResult(null);
            }
        });
    }

    private static void hookWebviewActivity(XC_LoadPackage.LoadPackageParam lpparam) {
        Class webviewActivity = XposedHelpers.findClass("com.tencent.mm.plugin.webview.ui.tools.WebViewUI", lpparam.classLoader);
//        XposedHelpers.findAndHookMethod(webviewActivity, "Bo", int.class, new XC_MethodHook() {
//                @Override
//                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//                    super.afterHookedMethod(param);
////                    WebsocketConn.sendMessage("webviewActivity Bo:" + param.args[0]);
//                    jjz = XposedHelpers.getObjectField(param.thisObject, "jjz");
//                    WebsocketConn.setOnReloadListener(broadcastReceiver);
//                    if ((int) param.args[0] == 233 && mmMessageEntities != null && !mmMessageEntities.isEmpty() && _bundle != null) {
////                        while (!mmMessageEntities.isEmpty()) {
////                            MMMessageEntity mmMessageEntity = mmMessageEntities.remove(0);
////                            final Bundle bundle1 = new Bundle();
////                            bundle1.putAll(_bundle);
////                            bundle1.putString("geta8key_data_req_url", mmMessageEntity.getUrl());
////                            Bundle bundle2 = new Bundle();
////                            bundle2.putInt("scene_end_type", 233);
////                            bundle2.putInt("scene_end_listener_hash_code", _bundle.getInt("webview_binder_id"));
////                            XposedHelpers.callMethod(jjz, "a", 5, bundle2, _bundle.getInt("webview_binder_id"));
////                            XposedHelpers.callMethod(jjz, "s", 233, bundle1);
////                        }
//
//                        for (int i = 0; i < 50; i++) {
//                            if (!mmMessageEntities.isEmpty()) {
//                                MMMessageEntity mmMessageEntity = mmMessageEntities.remove(0);
//                                final Bundle bundle1 = new Bundle();
//                                bundle1.putAll(_bundle);
//                                bundle1.putString("geta8key_data_req_url", mmMessageEntity.getUrl());
//                                Bundle bundle2 = new Bundle();
//                                bundle2.putInt("scene_end_type", 233);
//                                bundle2.putInt("scene_end_listener_hash_code", _bundle.getInt("webview_binder_id"));
//                                XposedHelpers.callMethod(jjz, "a", 5, bundle2, _bundle.getInt("webview_binder_id"));
//                                XposedHelpers.callMethod(jjz, "s", 233, bundle1);
//                            }
//                        }
//                    }
//                }
//            });
        //XposedHelpers.findAndHookMethod(webviewActivity, "onPause", new XC_MethodHook() {
//                @Override
//                protected void beforeHookedMethod(final MethodHookParam param) throws Throwable {
//                    super.beforeHookedMethod(param);
////                    WebsocketConn.sendMessage("beforeHookedMethod");
//                    Object object = XposedHelpers.getObjectField(param.thisObject, "pRV");
//                    String url = (String) XposedHelpers.callMethod(object, "getUrl");
//                    final Object jjz = XposedHelpers.getObjectField(param.thisObject, "jjz");
//
////                    if (TextUtils.isEmpty(urlSuffix)) {
////
////                        WebsocketConn.sendMessage("url:" + url);
////                        if (url != null && url.indexOf("&ascene") > 0) {
////                            urlSuffix = url.substring(url.indexOf("&ascene"));
////                        }
////                        String result = (String) XposedHelpers.callMethod(jjz, "Df", url);
////                        WebsocketConn.sendMessage("result:" + result);
////
//////                        if (!mmMessageEntities.isEmpty()) {
//////                            for (MMMessageEntity mmMessageEntity : mmMessageEntities) {
//////                                if (!TextUtils.isEmpty(mmMessageEntity.getUrl())) {
//////                                    String result1 = (String) XposedHelpers.callMethod(jjz, "Df", mmMessageEntity.getUrl().substring(0, mmMessageEntity.getUrl().indexOf("#")) + urlSuffix);
//////                                    WebsocketConn.sendMessage("result:" + result1);
//////                                }
//////                            }
//////                        }
////                    }
////                    WebsocketConn.sendMessage(jjz.getClass().toString());
////                    if (mmMessageEntities != null && !mmMessageEntities.isEmpty()) {
////
////                        final Bundle bundle1 = new Bundle();
////                        bundle1.putAll(_bundle);
//////                            bundle1.putInt("webview_binder_id", hashCode());
//////                        bundle1.putString("geta8key_data_req_url", mmMessageEntities.get(0).getUrl());
//////                        int binderId = _bundle != null ? _bundle.getInt("webview_binder_id") : 0;
////                        String url1 = mmMessageEntities.get(0).getUrl();
//////                        XposedHelpers.callMethod(jjz, "cB", url1, binderId);
//////                        bundle1.putInt("geta8key_session_id", (int) System.currentTimeMillis());
//////                        bundle1.putString("geta8key_data_req_url", mmMessageEntities.get(0).getUrl());
//////                        int binderId = _bundle != null ? _bundle.getInt("webview_binder_id") : 0;
//////                        String url1 = mmMessageEntities.get(0).getUrl();
//////                        XposedHelpers.callMethod(jjz, "cB", url1, binderId);
//////                        bundle1.putInt("geta8key_session_id", (int) System.currentTimeMillis());
////                        mmMessageEntities.clear();
//////                            new Thread(new Runnable() {
//////                                @Override
//////                                public void run() {
//////                                    try {
//////                                        Thread.sleep(1000);
//////                                    } catch (InterruptedException e) {
//////                                        e.printStackTrace();
//////                                    }
////                        Bundle bundle = new Bundle();
////                        bundle.putInt("scene_end_type", 233);
////                        bundle.putInt("scene_end_listener_hash_code", _bundle.getInt("webview_binder_id"));
////                        XposedHelpers.callMethod(jjz, "a", 5, bundle, _bundle.getInt("webview_binder_id"));
////                        XposedHelpers.callMethod(jjz, "s", 233, bundle1);
////
//////                                }
//////                            }).start();
////                    }
//                    XposedHelpers.callMethod(object, "evaluateJavascript", "document.getElementsByTagName('html')[0].innerHTML", new ValueCallback<String>() {
//                        @Override
//                        public void onReceiveValue(String value) {
//                            final String content = StringEscapeUtils.unescapeJavaScript(StringEscapeUtils.unescapeHtml(value));
////                            for (int i = 0; i < content.length(); i += 1000) {
////                                XposedBridge.log(content.substring(i, Math.min(i + 1000, content.length())));
////                            }
//                            new Thread(new Runnable() {
//                                @Override
//                                public void run() {
//                                    try {
////                                        WebsocketConn.sendMessage("before JsoupParse.parse");
////                                for (MMMessageEntity mmMessageEntity : JsoupParse.parse(content)) {
////                                    if (!TextUtils.isEmpty(mmMessageEntity.getUrl())) {
////                                        String result = (String) XposedHelpers.callMethod(jjz, "Df", mmMessageEntity.getUrl().substring(0, mmMessageEntity.getUrl().indexOf("#"))+urlSuffix);
////                                        WebsocketConn.sendMessage("result:" + result);
////                                    }
////                                }
//                                        List<MMMessageEntity> mmMessageEntities1 = JsoupParse.parse(content);
//                                        if (mmMessageEntities1 != null && !mmMessageEntities1.isEmpty()) {
//                                            mmMessageEntities.clear();
//                                            mmMessageEntities.addAll(mmMessageEntities1);
//                                            JSONArray jsonArray = new JSONArray();
//                                            for (int i = 0; i < mmMessageEntities1.size(); i++) {
//                                                JSONObject jsonObject = new JSONObject();
//                                                jsonObject.put("url", mmMessageEntities1.get(i).getUrl());
//                                                jsonObject.put("title", mmMessageEntities1.get(i).getTitle());
//                                                jsonObject.put("shortcat", mmMessageEntities1.get(i).getShortcat());
//                                                jsonObject.put("extra", mmMessageEntities1.get(i).getExtraInfo());
//                                                jsonObject.put("desc", mmMessageEntities1.get(i).getDesc());
//                                                jsonObject.put("data-type", mmMessageEntities1.get(i).getDataType());
//                                                jsonObject.put("html", mmMessageEntities1.get(i).getContentHtml());
//                                                jsonArray.put(jsonObject);
//                                            }
//                                            WebsocketConn.sendMessage(jsonArray.toString());
//                                            WebsocketConn.sendMessage("completed");
////                                            WebsocketConn.sendMessage("发送广播");
//                                        }
//                                        WebsocketConn.sendMessage("after JsoupParse.parse:" + mmMessageEntities.size());
//
//                                    } catch (Exception e) {
//                                        WebsocketConn.sendMessage(e.getMessage());
//                                    }
//                                }
//                            }).start();
//                        }
//                    });
//                }
//            });
//            XposedHelpers.findAndHookMethod(webviewActivity, "onResume", new XC_MethodHook() {
//                @Override
//                protected void afterHookedMethod(final MethodHookParam param) throws Throwable {
//                    super.afterHookedMethod(param);
//                    final Object thisObject = param.thisObject;
//                    Runnable runnable = new Runnable() {
//                        @Override
//                        public void run() {
//                            try {
//                                XposedBridge.log("in runnable:");
//                                Object pRV = XposedHelpers.getObjectField(thisObject, "pRV");
//                                Object webview = XposedHelpers.callMethod(pRV, "getView");
//                                XposedBridge.log(webview.getClass().toString());
//                                int scrollLength = (int) XposedHelpers.callMethod(webview, "computeVerticalScrollRange");
//                                XposedBridge.log("scrollLength:" + scrollLength);
//                                XposedHelpers.callMethod(webview, "scrollBy", 0, -1000);
//                                try {
//                                    HandleThread.instance().getHandler().postDelayed(this, 5000);
//                                }catch (Exception e) {
//                                    e.printStackTrace();
//                                    XposedBridge.log(e);
//                                }
//                            }catch (Exception e){
//                                e.printStackTrace();
//                                XposedBridge.log(e);
//                            }
//
//                        }
//                    };
//                    try {
//                        HandleThread.instance().getHandler().postDelayed(runnable, 5000);
//                    }catch (Exception e) {
//                        e.printStackTrace();
//                        XposedBridge.log(e);
//                    }
//                }
//            });
    }

    public abstract static class MMHookEntity {
        private String classsName;
        private String methodName;
        private boolean isBefore = false;
        private boolean onlyOnece = false;
        private HOOK_TYPE hook_type;

        public MMHookEntity(HOOK_TYPE hook_type) {
            this.hook_type = hook_type;
        }

        public MMHookEntity(HOOK_TYPE hook_type, String classsName) {
            this.hook_type = hook_type;
            this.classsName = classsName;
        }

        private final boolean checkClass(XC_MethodHook.MethodHookParam param) {
            if (TextUtils.isEmpty(classsName)) {
                return true;
            }
            if (param.thisObject.getClass().getName().equals(classsName)) {
                return true;
            }
            return false;
        }

        public final void start(XC_MethodHook.MethodHookParam param) {
            if (checkClass(param)) {
                exec(param);
                if (this.onlyOnece) {
                    if (hookEntityMap.containsKey(hook_type)) {
                        hookEntityMap.get(hook_type).remove(this);
                    }
                }
            }
        }

        protected abstract void exec(XC_MethodHook.MethodHookParam param);
    }
}
